package se.webbzon.boltzmann.login;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;

import org.omg.CORBA_2_3.portable.OutputStream;

public abstract class HTTPResponse implements DataInput, Readable, Closeable, AutoCloseable {
	
	// The maximal buffer size
	private static final int maxBufferSize = 1*1024*1024;
	
	// The data input stream associated with the underlying input stream
	private final DataInputStream dis;
	
	// The reader input stream associated with the underlying input stream
	private final BufferedReader reader;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public HTTPResponse(InputStream is) {
		InputStream markableInputStream; 
		if (is.markSupported())
			markableInputStream = is;
		else
			markableInputStream = new BufferedInputStream(is);
		dis = new DataInputStream(markableInputStream);
		reader = new BufferedReader(new InputStreamReader(markableInputStream));
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Returns a new input stream which can be used to read from this HTTP response. **/
	public InputStream acquireInputStream() {
		return dis;
	}
	
	/** Returns the number of cookies associated with this HTTP response. **/
	public abstract int cookies();
	
	/** Returns the (n+1):th cookie of this HTTP response. **/
	public abstract HTTPCookie getCookie(int n);
	
	public void writeTo(OutputStream os) throws IOException {
		int bufferSize = Math.min(maxBufferSize, dis.available());
		byte[] buffer = new byte[bufferSize];
		int available = bufferSize;
		while (available > 0) {
			dis.read(buffer, 0, available);
			os.write(buffer, 0, available);
			available = Math.min(maxBufferSize, dis.available());
		}
	}
	
	/** Reads a character from the HTTP response using UTF-8 encoding. **/
	public char readCharUTF() throws IOException {
		return (char) reader.read();
	}
	
	/** Reads a line of text from the HTTP response. If the end of the stream
	 * has been reached then null is returned. **/
	public String readLine() throws IOException  {
		return reader.readLine();
	}

	@Override public int read(CharBuffer cb) throws IOException {
		int available = Math.min(maxBufferSize, dis.available());
		if (available == 0)
			return -1;
		else if (available % 2 == 1)
			available--;
		byte[] bytes = new byte[available];
		ByteBuffer buffer = ByteBuffer.wrap(bytes);
		dis.readFully(bytes, 0, available);
		cb.put(buffer.asCharBuffer());
		return available / 2;
	}

	@Override public void close() throws IOException {
		dis.close();
	}

	@Override public boolean readBoolean() throws IOException {
		return dis.readBoolean();
	}

	@Override public byte readByte() throws IOException {
		return dis.readByte();
	}

	@Override public char readChar() throws IOException {
		return dis.readChar();
	}

	@Override public double readDouble() throws IOException {
		return dis.readDouble();
	}

	@Override public float readFloat() throws IOException {
		return dis.readFloat();
	}

	@Override public void readFully(byte[] b, int off, int len) throws IOException {
		dis.readFully(b,off,len);
	}

	@Override public void readFully(byte[] b) throws IOException {
		dis.readFully(b);
	}

	@Override public int readInt() throws IOException {
		return dis.readInt();
	}

	@Override public long readLong() throws IOException {
		return dis.readLong();
	}

	@Override public short readShort() throws IOException {
		return dis.readShort();
	}

	@Override public String readUTF() throws IOException {
		return dis.readUTF();
	}

	@Override public int readUnsignedByte() throws IOException {
		return dis.readUnsignedByte();
	}

	@Override public int readUnsignedShort() throws IOException {
		return dis.readUnsignedShort();
	}

	@Override public int skipBytes(int n) throws IOException {
		return dis.skipBytes(n);
	}

}
